Conversation
With this framing, I think Limits::max_image_width and Limits::max_image_height no longer need to be communicated to or handled by the ImageDecoder trait, because the external code can check ImageDecoder::dimensions() before invoking ImageDecoder::read_image(); only the memory limit (Limits::max_alloc) is essential. That being said, the current way Limits are handled by ImageDecoder isn't that awkward to implement, so to reduce migration costs keeping the current ImageDecoder::set_limits() API may be OK. |
|
A couple thoughts... I do like the idea of handling animation decoding with this same trait. To understand, are you thinking of "sequences" as being animations or also stuff like the multiple images stored in a TIFF file? Even just handling animation has some tricky cases though. For instance in PNG, the default image that you get if you treat the image as non-animated may be different from the first frame of the animation. We might need both a The addition of an |
It's a dyn-compatible way that achieves the goal of the constructor so it is actually an abstraction.
What do you by this? The main problem in I'm also not suggesting that calling |
|
@fintelia This now includes the other changes including to
|
|
I can't speak about image metadata, but I really don't like the new
Regarding rectangle decoding, I think it would be better if we force decoders to support arbitrary rects. That's because the current interface is actually less efficient by allowing decoder to support only certain rects. To read a specific rect that is not supported as is, However, most image formats are based on lines of block (macro pixels). So we can do a trick. Decode a line according to the too-large rect, and then only copy the pixels in the real rect to the output buffer. This reduces the memory overhead for unsupported rects from And if a format can't do the line-based trick for unsupported rects, then decoders should just allocate a temp buffer for the too-large rect and then crop (=copy what is needed). This is still just as efficient as the best For use cases where users can use rowpitch to ignore the exccess parts of the too-large rect, we could just have a method that gives back a preferred rect, which can be decoded very efficiently. So the API could look like this: trait ImageDecoder {
// ...
/// Returns a viewbox that contains all pixels of the given rect but can potentially be decoded more efficiently.
/// If rect decoding is not supported or no more-efficient rect exists, the given rect is returned as is.
fn preferred_viewbox(&self, viewbox: Rect) -> Rect {
viewbox // default impl
}
fn read_image_rect(&mut self, buf, viewbox) -> ImageResult {
Err(ImageError::Decoding(Decoding::RectDecodingNotSupported)) // or similar
}This API should make rect decoding easier to use, easier to implement, and allow for more efficient implementations. |
86c9194 to
cdc0363
Compare
That was one of the open questions, the argument you're presenting makes it clear it should return the layout and that's it. Renamed to
It's suppose to be to the full image. Yeah, that needs more documentation and pointers to the proper implementation. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
1a114c3 to
306c6d2
Compare
|
Resolving the naming question as |
e8d2713 to
4325060
Compare
|
@fintelia I understand this is too big for a code-depth review but I'd be interested in the directional input. Is the merging of 'animations' and simple images as well as the optimization hint methods convincing enough? Is the idea of returning data from As an aside, in wondermagick we basically find that sequence encoding is a missing API to match imagemagick. We can currently only do this with |
f6720de to
c677c88
Compare
|
Haven't been following this closely, but will try to give another round of feedback this week |
fintelia
left a comment
There was a problem hiding this comment.
Left a bunch of comments. Haven't had a chance to fully read/consider the image_reader_type.rs changes, but I like the direction this is going
| /// [`ImageDecoder::read_image`] with kind set to [`None`](crate::io::SequenceControl::None), | ||
| /// which is also treated as end of stream. This may be used by decoders which can not | ||
| /// determine the number of images in advance. | ||
| pub fn into_frames(mut self) -> Frames<'stream> { |
There was a problem hiding this comment.
We should be clear about whether this also applies to image sequences
| /// Result of [`ImageReader::decode_into`] that provides access to metadata. | ||
| pub struct DecodedImageMetadata<'reader> { | ||
| inner: &'reader mut (dyn ImageDecoder + 'reader), | ||
| attributes: &'reader DecodedImageAttributes, | ||
| metadata_buffers: &'reader mut MetadataBuffers, | ||
| } |
There was a problem hiding this comment.
I haven't had a chance to think it through in detail, but it might make sense to have this be a flat struct containing the metadata:
pub struct DecodeImageMetadata {
pub orientation: Option<Vec<u8>>,
pub exif: Option<Vec<u8>>,
...
}68b3037 to
0d413c2
Compare
0d413c2 to
278bb47
Compare
bb33923 to
d9ab806
Compare
Consolidates the variants so that all supported types of metadata must guarantee that the data is actually present. This merely requires the decoder to be able to seek; which is already usually the case and reasonably implementable. This will require support in: gif, png, webp
d9ab806 to
4500693
Compare
6e9a987 to
8e45a68
Compare
0a63889 to
e456c55
Compare
|
Awesome to see this land! 🎉 |
See #2245, the intended
ImageDecoderchanges.This changes the
ImageDecodertrait to fix some underlying issues. The main change is a clarification to the responsibilities; the trait is an interface from an implementor towards theimagelibrary. That is, the protocol established from its interface should allow us to drive the decoder into our buffers and our metadata. It is not optimized to be used by an external caller which should prefer the use ofImageReaderand other inherent methods instead.This is a work-in-progress, below motivates the changes and discusses open points.
ImageDecoder::peek_layoutencourages decoders to read headers after the constructor. This fixes the inherent problem we had with communicating limits. The sequences for internal use is roughly:ImageDecoder::read_image(&mut self)no longer consumesself. We no longer need the additionalboxedmethod and its trait work around, the trait is now dyn-compatible.Discussion
initpeek_layoutshould return the full layout information in a single struct. We have a similar open issue forpngin its own crate, and the related work fortiffis in the pipeline where itsBufferLayoutPreferencealready exists to be extended with said information.Review limits and remove its size bounds insofar as they can be checked against the communicated bounds in the metadata step by thesee: Replaceimageside.ImageDecoder::set_limitswithImageDecoder::set_allocation_limit#2709, Add an atomically shared allocation limit #27081.1, but it's not highly critical.read_imagethen switching to a sequence reader. But that is supposed to become mainly an adapter that implements the iterator protocol.ImageReaderwith a new interface to return some of it. That may be better suited for a separate PR though.CicpRgband apply it to a decodedDynamicImage.Cleanup
peek_layoutmore consistently afterread_imageread_imageis 'destructive' in all decoders, i.e. re-reading an image and reading an image beforeinitshould never access an incorrect part of the underlying stream but instead return an error. Affects pnm and qoi for instance where the read will interpret bytes based on the dimensions and color, which would be invalid before reading the header and only valid for one read.